using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading;
using HtmlAgilityPack;
using System.Text;
using System.Net;

namespace UniversityPhoneCrawler
{
    class Program
    {
        private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler
        {
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
            AllowAutoRedirect = true,
            MaxAutomaticRedirections = 5
        })
        {
            Timeout = TimeSpan.FromSeconds(30)
        };
        
        private static readonly HashSet<string> _phoneNumbers = new HashSet<string>();
        private static readonly Dictionary<string, HashSet<string>> _phoneUrlMap = new Dictionary<string, HashSet<string>>();
        private static readonly HashSet<string> _visitedUrls = new HashSet<string>();
        private static int _foundPhoneCount = 0;
        private static readonly object _lockObject = new object();
        private static CancellationTokenSource? _cancellationTokenSource;
        private static readonly Random _random = new Random();

        static async Task Main(string[] args)
        {
            Console.OutputEncoding = Encoding.UTF8;
            Console.WriteLine("欢迎使用高校电话号码爬虫程序！");
            
            string? keyword;
            if (args.Length > 0)
            {
                keyword = args[0];
            }
            else
            {
                Console.WriteLine("请输入搜索关键词（如：中国大学）：");
                keyword = Console.ReadLine();
            }

            if (string.IsNullOrWhiteSpace(keyword))
            {
                Console.WriteLine("关键词不能为空！");
                return;
            }

            _cancellationTokenSource = new CancellationTokenSource();
            Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;
                _cancellationTokenSource.Cancel();
                Console.WriteLine("\n正在取消搜索...");
            };

            try
            {
                await StartCrawling(keyword, _cancellationTokenSource.Token);
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("搜索已取消");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"发生错误：{ex.Message}");
            }

            if (!Console.IsInputRedirected)
            {
                Console.WriteLine("\n按任意键退出...");
                Console.ReadKey();
            }
        }

        private static async Task StartCrawling(string keyword, CancellationToken cancellationToken)
        {
            Console.WriteLine("开始搜索...\n");
            
            _httpClient.DefaultRequestHeaders.Clear();
            _httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
            _httpClient.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            _httpClient.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
            _httpClient.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
            _httpClient.DefaultRequestHeaders.Add("Pragma", "no-cache");
            
            var searchUrls = new[]
            {
                $"https://www.baidu.com/s?wd={Uri.EscapeDataString(keyword)}+联系电话",
                $"https://www.baidu.com/s?wd={Uri.EscapeDataString(keyword)}+电话号码",
                $"https://www.baidu.com/s?wd={Uri.EscapeDataString(keyword)}+招生办电话"
            };

            var allUrls = new HashSet<string>();
            foreach (var searchUrl in searchUrls)
            {
                if (cancellationToken.IsCancellationRequested) break;
                
                Console.WriteLine($"正在搜索: {searchUrl}");
                var urls = await GetSearchResults(searchUrl, cancellationToken);
                foreach (var url in urls)
                {
                    allUrls.Add(url);
                }
                
                await Task.Delay(TimeSpan.FromSeconds(_random.Next(2, 5)), cancellationToken);
            }
            
            Console.WriteLine($"找到 {allUrls.Count} 个搜索结果");
            
            var tasks = new List<Task>();
            var semaphore = new SemaphoreSlim(3);

            foreach (var url in allUrls)
            {
                if (cancellationToken.IsCancellationRequested) break;

                tasks.Add(Task.Run(async () =>
                {
                    try
                    {
                        await semaphore.WaitAsync(cancellationToken);
                        await ProcessUrl(url, cancellationToken);
                        await Task.Delay(TimeSpan.FromSeconds(_random.Next(1, 3)), cancellationToken);
                    }
                    finally
                    {
                        semaphore.Release();
                    }
                }, cancellationToken));
            }

            await Task.WhenAll(tasks);
            Console.WriteLine($"\n搜索完成，共找到 {_phoneNumbers.Count} 个电话号码");
            
            if (_phoneNumbers.Count > 0)
            {
                Console.WriteLine("\n=== 搜索结果 ===");
                foreach (var phone in _phoneNumbers)
                {
                    Console.WriteLine($"\n电话: {phone}");
                    Console.WriteLine("来源URL:");
                    foreach (var url in _phoneUrlMap[phone])
                    {
                        Console.WriteLine($"  {url}");
                    }
                }
            }
            else
            {
                Console.WriteLine("\n未找到任何电话号码。");
            }
        }

        private static async Task<List<string>> GetSearchResults(string searchUrl, CancellationToken cancellationToken)
        {
            var urls = new List<string>();
            try
            {
                var html = await _httpClient.GetStringAsync(searchUrl, cancellationToken);
                var doc = new HtmlDocument();
                doc.LoadHtml(html);

                var contentNodes = doc.DocumentNode.SelectNodes("//div[contains(@class, 'result')]") ??
                                 doc.DocumentNode.SelectNodes("//div[@class='c-container']") ??
                                 doc.DocumentNode.SelectNodes("//div[contains(@class, 'c-container')]");

                if (contentNodes != null)
                {
                    foreach (var node in contentNodes)
                    {
                        var linkNodes = node.SelectNodes(".//a[contains(@href, 'http')]");
                        if (linkNodes != null)
                        {
                            foreach (var linkNode in linkNodes)
                            {
                                var href = linkNode.GetAttributeValue("href", "");
                                if (!string.IsNullOrEmpty(href) && href.StartsWith("http") && !urls.Contains(href))
                                {
                                    urls.Add(href);
                                    Console.WriteLine($"找到URL: {href}");
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"获取搜索结果时出错：{ex.Message}");
            }

            return urls;
        }

        private static async Task ProcessUrl(string url, CancellationToken cancellationToken)
        {
            if (_visitedUrls.Contains(url) || _foundPhoneCount >= 50) return;

            try
            {
                var response = await _httpClient.GetAsync(url, cancellationToken);
                if (!response.IsSuccessStatusCode)
                {
                    Console.WriteLine($"访问 {url} 失败: {response.StatusCode}");
                    return;
                }

                var html = await response.Content.ReadAsStringAsync();
                var finalUrl = response.RequestMessage?.RequestUri?.ToString() ?? url;

                lock (_lockObject)
                {
                    _visitedUrls.Add(url);
                    Console.WriteLine($"正在处理: {finalUrl}");
                }

                ExtractPhoneNumbers(html, finalUrl);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"处理URL {url} 时出错：{ex.Message}");
            }
        }

        private static void ExtractPhoneNumbers(string html, string url)
        {
            var phonePatterns = new[]
            {
                @"(?:[\(（]?\d{3,4}[\)）]?[-－]?)?\d{7,8}",
                @"(?:[\(（]?\d{3,4}[\)）]?[-－]?)?\d{3}[-－]?\d{4}",
                @"1[3-9]\d{9}",
                @"(?:[\(（]?\d{3,4}[\)）]?[-－]?)\d{3,4}[-－]?\d{4}",
                @"(?:电话|联系方式|咨询|招生)[：:]\s*\d+[-－]?\d+"
            };

            foreach (var pattern in phonePatterns)
            {
                var matches = Regex.Matches(html, pattern);
                foreach (Match match in matches)
                {
                    var phone = match.Value;
                    phone = Regex.Replace(phone, @"[^0-9]", "");
                    
                    if ((phone.Length == 11 && phone.StartsWith("1")) ||
                        (phone.Length >= 7 && phone.Length <= 12))
                    {
                        lock (_lockObject)
                        {
                            if (_phoneNumbers.Count >= 50) return;
                            if (!_phoneNumbers.Contains(phone))
                            {
                                _phoneNumbers.Add(phone);
                                if (!_phoneUrlMap.ContainsKey(phone))
                                {
                                    _phoneUrlMap[phone] = new HashSet<string>();
                                }
                                _phoneUrlMap[phone].Add(url);
                                _foundPhoneCount++;
                                Console.WriteLine($"找到新电话号码: {phone}");
                            }
                        }
                    }
                }
            }
        }
    }
} 